{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 224
    },
    "colab_type": "code",
    "id": "UF3yuopkSW0A",
    "outputId": "9783c242-3e97-4ef0-a704-146b966894a6"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--2020-08-06 10:20:39--  https://raw.githubusercontent.com/propublica/compas-analysis/master/compas-scores-two-years.csv\n",
      "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ...\n",
      "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... connected.\n",
      "HTTP request sent, awaiting response... 200 OK\n",
      "Length: 2546489 (2.4M) [text/plain]\n",
      "Saving to: ‘compas-scores-two-years.csv’\n",
      "\n",
      "\r",
      "          compas-sc   0%[                    ]       0  --.-KB/s               \r",
      "compas-scores-two-y 100%[===================>]   2.43M  14.4MB/s    in 0.2s    \n",
      "\n",
      "2020-08-06 10:20:40 (14.4 MB/s) - ‘compas-scores-two-years.csv’ saved [2546489/2546489]\n",
      "\n"
     ]
    }
   ],
   "source": [
    "!wget https://raw.githubusercontent.com/propublica/compas-analysis/master/compas-scores-two-years.csv\n",
    "import pandas as pd\n",
    "date_cols = [\n",
    "    'compas_screening_date', 'c_offense_date',\n",
    "    'c_arrest_date', 'r_offense_date', \n",
    "    'vr_offense_date', 'screening_date',\n",
    "    'v_screening_date', 'c_jail_in',\n",
    "    'c_jail_out', 'dob', 'in_custody', \n",
    "    'out_custody'\n",
    "]\n",
    "data = pd.read_csv(\n",
    "    'compas-scores-two-years.csv',\n",
    "    parse_dates=date_cols\n",
    ")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "sLLJgrpfSZtf"
   },
   "outputs": [],
   "source": [
    "import datetime\n",
    "indexes = data.compas_screening_date <= pd.Timestamp(datetime.date(2014, 4, 1))\n",
    "assert indexes.sum() == 6216\n",
    "data = data[indexes]\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 224
    },
    "colab_type": "code",
    "id": "1hGtkoiJSbTN",
    "outputId": "992c1197-5a3c-40d8-ed8a-56c37e01039e"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Requirement already satisfied: category-encoders in /usr/local/lib/python3.6/dist-packages (2.2.2)\n",
      "Requirement already satisfied: scikit-learn>=0.20.0 in /usr/local/lib/python3.6/dist-packages (from category-encoders) (0.22.2.post1)\n",
      "Requirement already satisfied: statsmodels>=0.9.0 in /usr/local/lib/python3.6/dist-packages (from category-encoders) (0.10.2)\n",
      "Requirement already satisfied: scipy>=1.0.0 in /usr/local/lib/python3.6/dist-packages (from category-encoders) (1.4.1)\n",
      "Requirement already satisfied: numpy>=1.14.0 in /usr/local/lib/python3.6/dist-packages (from category-encoders) (1.18.5)\n",
      "Requirement already satisfied: patsy>=0.5.1 in /usr/local/lib/python3.6/dist-packages (from category-encoders) (0.5.1)\n",
      "Requirement already satisfied: pandas>=0.21.1 in /usr/local/lib/python3.6/dist-packages (from category-encoders) (1.0.5)\n",
      "Requirement already satisfied: joblib>=0.11 in /usr/local/lib/python3.6/dist-packages (from scikit-learn>=0.20.0->category-encoders) (0.16.0)\n",
      "Requirement already satisfied: six in /usr/local/lib/python3.6/dist-packages (from patsy>=0.5.1->category-encoders) (1.15.0)\n",
      "Requirement already satisfied: python-dateutil>=2.6.1 in /usr/local/lib/python3.6/dist-packages (from pandas>=0.21.1->category-encoders) (2.8.1)\n",
      "Requirement already satisfied: pytz>=2017.2 in /usr/local/lib/python3.6/dist-packages (from pandas>=0.21.1->category-encoders) (2018.9)\n"
     ]
    }
   ],
   "source": [
    "!pip install category-encoders"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "nOWV7G65Gjqj"
   },
   "outputs": [],
   "source": [
    "def confusion_metrics(actual, scores, threshold):\n",
    "    y_predicted = scores.apply(\n",
    "        lambda x: x >= threshold\n",
    "    ).values\n",
    "    y_true = actual.values\n",
    "    TP = (\n",
    "        (y_true==y_predicted) & \n",
    "        (y_predicted==1)\n",
    "    ).astype(int)\n",
    "    FP = (\n",
    "        (y_true!=y_predicted) &\n",
    "        (y_predicted==1)\n",
    "    ).astype(int)\n",
    "    TN = (\n",
    "        (y_true==y_predicted) &\n",
    "        (y_predicted==0)\n",
    "    ).astype(int)\n",
    "    FN = (\n",
    "        (y_true!=y_predicted) &\n",
    "        (y_predicted==0)\n",
    "    ).astype(int)\n",
    "    return TP, FP, TN, FN\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "K3qiEKETSMOE"
   },
   "outputs": [],
   "source": [
    "def calculate_impacts(data, sensitive_column='race', recid_col='is_recid', score_col='decile_score.1', threshold=5.0):\n",
    "    if sensitive_column == 'race':\n",
    "      norm_group = 'Caucasian'\n",
    "    elif sensitive_column == 'sex':\n",
    "      norm_group = 'Male'\n",
    "    else:\n",
    "      raise ValueError('sensitive column not implemented')\n",
    "    TP, FP, TN, FN = confusion_metrics(\n",
    "        actual=data[recid_col],\n",
    "        scores=data[score_col],\n",
    "        threshold=threshold\n",
    "    )\n",
    "    impact = pd.DataFrame(\n",
    "        data=np.column_stack([\n",
    "              FP, TN, FN, TN,\n",
    "              data[sensitive_column].values, \n",
    "              data[recid_col].values,\n",
    "              data[score_col].values / 10.0\n",
    "             ]),\n",
    "        columns=['FP', 'TP', 'FN', 'TN', 'sensitive', 'reoffend', 'score']\n",
    "    ).groupby(by='sensitive').agg({\n",
    "        'reoffend': 'sum', 'score': 'sum',\n",
    "        'sensitive': 'count', \n",
    "        'FP': 'sum', 'TP': 'sum', 'FN': 'sum', 'TN': 'sum'\n",
    "    }).rename(\n",
    "        columns={'sensitive': 'N'}\n",
    "    )\n",
    "    impact['FPR'] = impact['FP'] / (impact['FP'] + impact['TN'])\n",
    "    impact['FNR'] = impact['FN'] / (impact['FN'] + impact['TP'])\n",
    "    impact['reoffend'] = impact['reoffend'] / impact['N']\n",
    "    impact['score'] = impact['score'] / impact['N']\n",
    "    impact['DFP'] = impact['FPR'] / impact.loc[norm_group, 'FPR']\n",
    "    impact['DFN'] = impact['FNR'] / impact.loc[norm_group, 'FNR']\n",
    "    return impact.drop(columns=['FP', 'TP', 'FN', 'TN'])\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "GTE2xbhPSOw9"
   },
   "outputs": [],
   "source": [
    "from sklearn.feature_extraction.text import CountVectorizer\n",
    "from category_encoders.one_hot import OneHotEncoder\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.preprocessing import StandardScaler\n",
    "\n",
    "charge_desc = data['c_charge_desc'].apply(lambda x: x if isinstance(x, str) else '')\n",
    "count_vectorizer = CountVectorizer(\n",
    "    max_df=0.85, stop_words='english',\n",
    "    max_features=100, decode_error='ignore'\n",
    ")\n",
    "charge_desc_features = count_vectorizer.fit_transform(charge_desc)\n",
    "\n",
    "one_hot_encoder = OneHotEncoder()\n",
    "charge_degree_features = one_hot_encoder.fit_transform(\n",
    "    data['c_charge_degree']\n",
    ")\n",
    "\n",
    "data['race_black'] = data['race'].apply(lambda x: x == 'African-American').astype(int)\n",
    "stratification = data['race_black'] + (data['is_recid']).astype(int) * 2\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "P7vdYFUHSRzi"
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "y = data['is_recid']\n",
    "X = pd.DataFrame(\n",
    "    data=np.column_stack(\n",
    "        [data[['juv_fel_count', 'juv_misd_count',\n",
    " 'juv_other_count', 'priors_count', 'days_b_screening_arrest']], \n",
    "          charge_degree_features, \n",
    "          charge_desc_features.todense()\n",
    "        ]\n",
    "    ),\n",
    "    columns=['juv_fel_count', 'juv_misd_count', 'juv_other_count', 'priors_count', 'days_b_screening_arrest'] \\\n",
    "    + one_hot_encoder.get_feature_names() \\\n",
    "    + count_vectorizer.get_feature_names(),\n",
    "    index=data.index\n",
    ")\n",
    "X['jailed_days'] = (data['c_jail_out'] - data['c_jail_in']).apply(lambda x: abs(x.days))\n",
    "X['waiting_jail_days'] = (data['c_jail_in'] - data['c_offense_date']).apply(lambda x: abs(x.days))\n",
    "X['waiting_arrest_days'] = (data['c_arrest_date'] - data['c_offense_date']).apply(lambda x: abs(x.days))\n",
    "X.fillna(0, inplace=True)\n",
    "\n",
    "columns = list(X.columns)\n",
    "X_train, X_test, y_train, y_test = train_test_split(\n",
    "    X, y, test_size=0.33,\n",
    "    random_state=42,\n",
    "    stratify=stratification\n",
    ")  # we stratify by black and the target\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "hWtkwLMvSiRr"
   },
   "outputs": [],
   "source": [
    "import jax.numpy as jnp\n",
    "from jax import grad, jit, vmap, ops, lax\n",
    "import numpy as onp\n",
    "import numpy.random as npr\n",
    "import random\n",
    "from tqdm import trange\n",
    "from sklearn.base import ClassifierMixin\n",
    "from sklearn.preprocessing import StandardScaler\n",
    "\n",
    "\n",
    "class JAXLearner(ClassifierMixin):\n",
    "  def __init__(self, layer_sizes=[10, 5, 1], epochs=20, batch_size=500, lr=1e-2):\n",
    "    self.params = self.construct_network(layer_sizes)\n",
    "    self.perex_grads = jit(grad(self.error))\n",
    "    self.epochs = epochs\n",
    "    self.batch_size = batch_size\n",
    "    self.lr = lr\n",
    "\n",
    "  @staticmethod\n",
    "  def construct_network(layer_sizes=[10, 5, 1]):\n",
    "    '''Please make sure your final layer corresponds to targets in dimensions.\n",
    "    '''\n",
    "    def init_layer(n_in, n_out):\n",
    "      W = npr.randn(n_in, n_out)\n",
    "      b = npr.randn(n_out,)\n",
    "      return W, b\n",
    "      \n",
    "    return list(map(init_layer, layer_sizes[:-1], layer_sizes[1:]))\n",
    "\n",
    "  @staticmethod\n",
    "  def sigmoid(X):  # or tanh\n",
    "    return 1/(1+jnp.exp(-X))\n",
    "\n",
    "  def _predict(self, inputs):\n",
    "    for W, b in self.params:\n",
    "      outputs = jnp.dot(inputs, W) + b\n",
    "      inputs = self.sigmoid(outputs)\n",
    "    return outputs\n",
    "\n",
    "  def predict(self, inputs):\n",
    "    inputs = self.standard_scaler.transform(inputs)\n",
    "    return onp.asarray(self._predict(inputs))\n",
    "\n",
    "  @staticmethod\n",
    "  def mse(preds, targets, other=None):\n",
    "    return jnp.sqrt(jnp.sum((preds - targets)**2))\n",
    "\n",
    "  @staticmethod\n",
    "  def penalized_mse(preds, targets, sensitive):\n",
    "    err = jnp.sum((preds - targets)**2)\n",
    "    err_s = jnp.sum((preds * sensitive - targets * sensitive)**2)\n",
    "    penalty = jnp.clip(err_s / err, 1.0, 2.0)\n",
    "    return err * penalty\n",
    "\n",
    "  def error(self, params, inputs, targets, sensitive):\n",
    "      preds = self._predict(inputs)\n",
    "      return self.penalized_mse(preds, targets, sensitive)\n",
    "\n",
    "  def fit(self, X, y, sensitive):\n",
    "    self.standard_scaler = StandardScaler()\n",
    "    X = self.standard_scaler.fit_transform(X)\n",
    "    N = X.shape[0]\n",
    "    indexes = list(range(N))\n",
    "    steps_per_epoch = N // self.batch_size\n",
    "\n",
    "    for epoch in trange(self.epochs, desc='training'):\n",
    "        random.shuffle(indexes)\n",
    "        index_offset = 0\n",
    "        for step in trange(steps_per_epoch, desc='iteration'):\n",
    "            grads = self.perex_grads(\n",
    "                self.params, \n",
    "                X[indexes[index_offset:index_offset+self.batch_size], :], \n",
    "                y[indexes[index_offset:index_offset+self.batch_size]],\n",
    "                sensitive[indexes[index_offset:index_offset+self.batch_size]]\n",
    "            )\n",
    "            # print(grads)\n",
    "            self.params = [(W - self.lr * dW, b - self.lr * db)\n",
    "                      for (W, b), (dW, db) in zip(self.params, grads)]\n",
    "            index_offset += self.batch_size\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 714
    },
    "colab_type": "code",
    "id": "a45RcwvtSqhP",
    "outputId": "6e1625e7-e527-4ce0-d401-94722cdb5cfb"
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "training:   0%|          | 0/20 [00:00<?, ?it/s]\n",
      "iteration: 100%|██████████| 8/8 [00:00<00:00, 460.32it/s]\n",
      "\n",
      "iteration: 100%|██████████| 8/8 [00:00<00:00, 576.52it/s]\n",
      "\n",
      "iteration: 100%|██████████| 8/8 [00:00<00:00, 627.77it/s]\n",
      "\n",
      "iteration: 100%|██████████| 8/8 [00:00<00:00, 650.54it/s]\n",
      "\n",
      "iteration: 100%|██████████| 8/8 [00:00<00:00, 737.82it/s]\n",
      "training:  25%|██▌       | 5/20 [00:00<00:00, 48.03it/s]\n",
      "iteration: 100%|██████████| 8/8 [00:00<00:00, 638.95it/s]\n",
      "\n",
      "iteration: 100%|██████████| 8/8 [00:00<00:00, 782.45it/s]\n",
      "\n",
      "iteration: 100%|██████████| 8/8 [00:00<00:00, 601.05it/s]\n",
      "\n",
      "iteration: 100%|██████████| 8/8 [00:00<00:00, 807.22it/s]\n",
      "\n",
      "iteration: 100%|██████████| 8/8 [00:00<00:00, 628.20it/s]\n",
      "\n",
      "iteration: 100%|██████████| 8/8 [00:00<00:00, 640.28it/s]\n",
      "training:  55%|█████▌    | 11/20 [00:00<00:00, 49.09it/s]\n",
      "iteration: 100%|██████████| 8/8 [00:00<00:00, 691.06it/s]\n",
      "\n",
      "iteration: 100%|██████████| 8/8 [00:00<00:00, 643.61it/s]\n",
      "\n",
      "iteration: 100%|██████████| 8/8 [00:00<00:00, 808.37it/s]\n",
      "\n",
      "iteration: 100%|██████████| 8/8 [00:00<00:00, 686.75it/s]\n",
      "\n",
      "iteration: 100%|██████████| 8/8 [00:00<00:00, 810.38it/s]\n",
      "\n",
      "iteration: 100%|██████████| 8/8 [00:00<00:00, 682.69it/s]\n",
      "training:  85%|████████▌ | 17/20 [00:00<00:00, 50.15it/s]\n",
      "iteration: 100%|██████████| 8/8 [00:00<00:00, 682.21it/s]\n",
      "\n",
      "iteration: 100%|██████████| 8/8 [00:00<00:00, 667.15it/s]\n",
      "\n",
      "iteration: 100%|██████████| 8/8 [00:00<00:00, 605.59it/s]\n",
      "training: 100%|██████████| 20/20 [00:00<00:00, 50.86it/s]\n"
     ]
    }
   ],
   "source": [
    "sensitive_train = X_train.join(\n",
    "    data, rsuffix='_right'\n",
    ")['race_black']\n",
    "jax_learner = JAXLearner([X.values.shape[1], 100, 1])\n",
    "jax_learner.fit(\n",
    "    X_train.values,\n",
    "    y_train.values,\n",
    "    sensitive_train.values\n",
    ")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "Kl5qF97cS9OL"
   },
   "outputs": [],
   "source": [
    "X_predicted = pd.DataFrame(\n",
    "    data=jax_learner.predict(\n",
    "        X_test.values\n",
    "    ) * 10,\n",
    "    columns=['score'], \n",
    "    index=X_test.index\n",
    ").join(\n",
    "    data[['sex', 'race', 'is_recid']], \n",
    "    rsuffix='_right'\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 419
    },
    "colab_type": "code",
    "id": "KakKvALIVCLj",
    "outputId": "0734b142-471e-4750-9e36-3586a22b2ce4"
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>score</th>\n",
       "      <th>sex</th>\n",
       "      <th>race</th>\n",
       "      <th>is_recid</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>6553</th>\n",
       "      <td>45.214291</td>\n",
       "      <td>Male</td>\n",
       "      <td>Caucasian</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1441</th>\n",
       "      <td>-42.396706</td>\n",
       "      <td>Male</td>\n",
       "      <td>African-American</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2306</th>\n",
       "      <td>-60.853111</td>\n",
       "      <td>Male</td>\n",
       "      <td>Caucasian</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>504</th>\n",
       "      <td>-12.313410</td>\n",
       "      <td>Male</td>\n",
       "      <td>Caucasian</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5212</th>\n",
       "      <td>27.922726</td>\n",
       "      <td>Male</td>\n",
       "      <td>African-American</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>...</th>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6118</th>\n",
       "      <td>46.818214</td>\n",
       "      <td>Male</td>\n",
       "      <td>Caucasian</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>607</th>\n",
       "      <td>9.088397</td>\n",
       "      <td>Female</td>\n",
       "      <td>Caucasian</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2596</th>\n",
       "      <td>-12.438754</td>\n",
       "      <td>Female</td>\n",
       "      <td>Caucasian</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3204</th>\n",
       "      <td>-87.487778</td>\n",
       "      <td>Male</td>\n",
       "      <td>African-American</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4692</th>\n",
       "      <td>36.880836</td>\n",
       "      <td>Female</td>\n",
       "      <td>African-American</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>2052 rows × 4 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "          score     sex              race  is_recid\n",
       "6553  45.214291    Male         Caucasian         0\n",
       "1441 -42.396706    Male  African-American         1\n",
       "2306 -60.853111    Male         Caucasian         0\n",
       "504  -12.313410    Male         Caucasian         0\n",
       "5212  27.922726    Male  African-American         0\n",
       "...         ...     ...               ...       ...\n",
       "6118  46.818214    Male         Caucasian         1\n",
       "607    9.088397  Female         Caucasian         0\n",
       "2596 -12.438754  Female         Caucasian         1\n",
       "3204 -87.487778    Male  African-American         1\n",
       "4692  36.880836  Female  African-American         0\n",
       "\n",
       "[2052 rows x 4 columns]"
      ]
     },
     "execution_count": 31,
     "metadata": {
      "tags": []
     },
     "output_type": "execute_result"
    }
   ],
   "source": [
    "X_predicted"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 266
    },
    "colab_type": "code",
    "id": "D_T4dZAYS-sP",
    "outputId": "b5399c87-dfef-4aa2-a7b8-c1780749eeee"
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>reoffend</th>\n",
       "      <th>score</th>\n",
       "      <th>N</th>\n",
       "      <th>FPR</th>\n",
       "      <th>FNR</th>\n",
       "      <th>DFP</th>\n",
       "      <th>DFN</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>sensitive</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>African-American</th>\n",
       "      <td>0.471042</td>\n",
       "      <td>-0.948788</td>\n",
       "      <td>1036</td>\n",
       "      <td>0.385036</td>\n",
       "      <td>0.487842</td>\n",
       "      <td>0.868148</td>\n",
       "      <td>1.401687</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Asian</th>\n",
       "      <td>0.222222</td>\n",
       "      <td>1.802761</td>\n",
       "      <td>9</td>\n",
       "      <td>0.571429</td>\n",
       "      <td>0.250000</td>\n",
       "      <td>1.288410</td>\n",
       "      <td>0.718310</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Caucasian</th>\n",
       "      <td>0.335188</td>\n",
       "      <td>-0.308411</td>\n",
       "      <td>719</td>\n",
       "      <td>0.443515</td>\n",
       "      <td>0.348039</td>\n",
       "      <td>1.000000</td>\n",
       "      <td>1.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Hispanic</th>\n",
       "      <td>0.293478</td>\n",
       "      <td>0.121934</td>\n",
       "      <td>184</td>\n",
       "      <td>0.500000</td>\n",
       "      <td>0.329897</td>\n",
       "      <td>1.127358</td>\n",
       "      <td>0.947873</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Native American</th>\n",
       "      <td>0.500000</td>\n",
       "      <td>0.963587</td>\n",
       "      <td>2</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>0.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Other</th>\n",
       "      <td>0.294118</td>\n",
       "      <td>-0.626596</td>\n",
       "      <td>102</td>\n",
       "      <td>0.430556</td>\n",
       "      <td>0.267857</td>\n",
       "      <td>0.970781</td>\n",
       "      <td>0.769618</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                  reoffend     score     N  ...       FNR       DFP       DFN\n",
       "sensitive                                   ...                              \n",
       "African-American  0.471042 -0.948788  1036  ...  0.487842  0.868148  1.401687\n",
       "Asian             0.222222  1.802761     9  ...  0.250000  1.288410  0.718310\n",
       "Caucasian         0.335188 -0.308411   719  ...  0.348039  1.000000  1.000000\n",
       "Hispanic          0.293478  0.121934   184  ...  0.329897  1.127358  0.947873\n",
       "Native American   0.500000  0.963587     2  ...  0.000000  0.000000  0.000000\n",
       "Other             0.294118 -0.626596   102  ...  0.267857  0.970781  0.769618\n",
       "\n",
       "[6 rows x 7 columns]"
      ]
     },
     "execution_count": 32,
     "metadata": {
      "tags": []
     },
     "output_type": "execute_result"
    }
   ],
   "source": [
    "calculate_impacts(X_predicted, score_col='score')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "IPWiMPpyTUI7"
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "colab": {
   "collapsed_sections": [],
   "name": "recidivism + active learning",
   "provenance": []
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
